using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Windows.Forms;

using Koossery.MVCwin.Actions.itf;
using Koossery.MVCwin.Controller.itf;
using Koossery.MVCwin.ControllerManager.itf;
using Koossery.MVCwin.Properties;
using Koossery.MVCwin.Util;
using Koossery.MVCwin.Views.impl;
using Koossery.MVCwin.Views.itf;

using log4net;
using System.Reflection;

namespace Koossery.MVCwin.ControllerManager.impl
{
    /// <author>Mohamadou Yacoubou</author>
    /// <summary>
    /// An implementation of controller manager, it's manages applications controllers
    /// </summary>
	public class ControllerManager : IControllerManager
	{
		#region Static Fields

		private static readonly ILog log = log4net.LogManager.GetLogger("Koossery.MVCwin");

		#endregion Static Fields

		#region Fields

		/// <summary>
		/// BackgroundWorker for asynchronous actions execution
		/// </summary>
		private BackgroundWorker _bgAsyncMethods;

		/// <summary>
		/// Set of application's controllers
		/// </summary>
		private IDictionary<string, IController> _controllers;

		/// <summary>
		/// The default controller
		/// </summary>
		private string _defaultController;

		/// <summary>
		/// The former view
		/// </summary>
		private IView _formerView;

		/// <summary>
		/// Set of application's views
		/// </summary>        
		private IDictionary<string, IView> _views;

		#endregion Fields

		#region Constructors

		public ControllerManager()
		{
			//BackgroundWorker intialization
			_bgAsyncMethods = new BackgroundWorker();

			//BackgroundWorker configuration
			BgAsyncMethods.WorkerReportsProgress = true;
			BgAsyncMethods.WorkerSupportsCancellation = true;

			//Souscription to the backgroundWorker's event
			BgAsyncMethods.DoWork += new DoWorkEventHandler(BgAsyncMethods_DoWork);
			BgAsyncMethods.RunWorkerCompleted += new RunWorkerCompletedEventHandler(BgAsyncMethods_RunWorkerCompleted);
		}

		#endregion Constructors

		#region Public Properties

		/// <summary>
		/// <see cref="_bgAsyncMethods"/>
		/// </summary>
		public BackgroundWorker BgAsyncMethods
		{
			get { return _bgAsyncMethods; }
		}

		/// <summary>
		/// <see cref="_controllers"/>
		/// </summary>
		public IDictionary<string, IController> Controllers
		{
			set
			{
			    if (_controllers != null)
			    {
			        log.Error(Resources.InitializedSessionException);
			        throw new Exception(Resources.InitializedSessionException);
			    }
			    _controllers = value;
			}
		}

		/// <summary>
		/// <see cref="_defaultController"/>
		/// </summary>
		public string DefaultController
		{
			set
			{
			    if (_defaultController != null)
			    {
			        log.Error(Resources.InitializedSessionException);
			        throw new Exception(Resources.InitializedSessionException);
			    }
			    _defaultController = value;
			}
		}

		/// <summary>
		/// <see cref="_views"/>
		/// </summary>
		public IDictionary<string, IView> Views
		{
			set
			{
			    if (_views != null)
			    {
			        log.Error(Resources.InitializedSessionException);
			        throw new Exception(Resources.InitializedSessionException);
			    }
			    _views = value;
			}
		}

		#endregion Public Properties

		#region Private Methods

		/// <summary>
		/// Execute asynchronous actions. It's executed in a different thread
		/// </summary>
		/// <param name="sender">The sender object</param>
		/// <param name="e">Event's arguments</param>
		void BgAsyncMethods_DoWork(object sender, DoWorkEventArgs e)
		{
			BgAsyncMethods.ReportProgress(1);

			//Getting arguments
			object[] parameters = e.Argument as object[];
			//Getting controller and action to invoke
			IController controller = parameters[0] as IController;
			string actionName = parameters[1] as string;

			//Executing action
			IActionResult actionResult = (IActionResult)controller.GetType().GetMethod(actionName).Invoke(controller, null);

			   		//Building de result object
			object[] results = new object[] { actionResult, controller.GetType().Name };

			//Saving the result object for the next step
			e.Result = results;
		}

		/// <summary>
		/// Executed when the background process has ended. 
		/// This method is executed by the principal thread
		/// </summary>
		/// <param name="sender">Sender object</param>
		/// <param name="e">Event's Arguments</param>
		void BgAsyncMethods_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
		{
			//error occurs or background process is cancelled
			if (e.Error != null || e.Cancelled) return;

			//Background process execution has ended successfully.
			//Executing the next command
			object[] results = e.Result as object[];
			IActionResult actionResult = results[0] as IActionResult;
			string controllerName = results[1] as string;

			//Execute the result
            actionResult.ExecuteResult(this);			
		}
		
		#endregion Private Methods

		#region Public Methods

		/// <summary>
		/// Invoke the Execute action of the controller
		/// </summary>
		/// <param name="controllerName">The controller's name</param>
		public void Invoke(string controllerName)
		{
			Invoke(controllerName, null);
		}

		/// <summary>
		/// Invoke an action
		/// </summary>
		/// <param name="controllerName">The controller's name</param>
		/// <param name="actionName">The action's name</param>
		public void Invoke(string controllerName, string actionName)
		{
			IController controller = null;
			IActionResult actionResult = null;

			if (controllerName == null)
			{
			    //Default controller is invoked when null is passed as an argument
			    controllerName = _defaultController;
			}

			//Controller is missing
			if (_controllers[controllerName] == null)
			{
			    log.Error(string.Format(Resources.ControllerMissingException, controllerName));
			    throw new Exception(string.Format(Resources.ControllerMissingException, controllerName));
			}

			//Getting the controller to invoke
			controller = _controllers[controllerName];
			try
			{
			    //if null is passed as an argument then the default Execute action is executed
			    if (string.IsNullOrEmpty(actionName)) actionName = Constants.Execute;
                
			    //Getting the action mode
                bool asynchMode = controller.IsAnAsynchronousAction(actionName);
                                
                //If the action should be executed asynchronously
                if (asynchMode)
			    {
			        //building parameter
			        object[] parameters = new object[] { controller, actionName };
                    BgAsyncMethods.RunWorkerAsync(parameters);
			        return;
			    }

			    //No background process is required the action is simply executed
			    actionResult = (IActionResult)controller.GetType().GetMethod(actionName).Invoke(controller, null);

			    //Execute the result
                actionResult.ExecuteResult(this);
			    //ShowViewOrInvokeController(actionResult, controllerName);
			}            
			catch (Exception ex)
			{
			    log.Error(ex.ToString());
			    throw new Exception(string.Format(Resources.ActionExecutionException, actionName, ex.Message));
			}
		}

		#endregion Public Methods

        #region IControllerManager Membres


        public void RenderView(string viewName)
        {
            IView view = null;
            //The view doesn't exist
            if (_views[viewName] == null)
            {
                log.Error(string.Format(Resources.ViewMissingException, viewName));

                //The view is missing
                throw new Exception(string.Format(Resources.ViewMissingException, viewName));
            }

            //Getting the view to display
            view = _views[viewName];

            if (_formerView == null)
            {
                //First view to be displayed by the controller manager
                _formerView = view;
                view.ControllerManager = this;
                view.DisplayView();

                //Running the application
                Application.Run((BaseView)view);
            }
            else
            {
                view.ControllerManager = this;
                if (_formerView != view)
                {
                    if (!_formerView.IsChildForm) _formerView.HideView();
                    _formerView = view;
                }
                view.DisplayView();
            }
        }
        
        #endregion
    }
}